﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace RichList
{

    public partial class RichListBox: UserControl
    {
        //================ 常量 =====================
        internal const int ITEM_HEIGHT = 50;
        internal const int SCROLL_WEIDTH = 20;
        internal Color ODD_COLOR = Color.FromArgb(227, 244, 255);
        internal Color EVEN_COLOR = Color.FromArgb(217, 238, 254);
        internal Color HOVER_COLOR = Color.FromArgb(202, 232, 248);
        internal Color SELECTED_COLOR = Color.FromArgb(189, 227, 247);
        
        //================成员变量====================
        ItemList _Items;
        int _selected_index;
        int _hovered_index;

        public int SelectedInex
        {
            get
            {
                return _selected_index;
            }
            set
            {
                int old = _selected_index;
                _selected_index = value;
                this.UpdateItemAt(old);
                this.UpdateItemAt(value);
            }
        }
        public int HoveredInex
        {
            get
            {
                return _hovered_index;
            }
            set
            {
                if (value == _hovered_index)
                {
                    return;
                }
                int old = _hovered_index;
                _hovered_index = value;
                this.UpdateItemAt(old);
                this.UpdateItemAt(value);
            }
        }
        public RichListItem Selected
        {
            get
            {
                if (_selected_index == -1)
                {
                    return null;
                }
                return _Items[_Items.GetIdByIndex(_selected_index)];
            }
        }

        //================构造函数==================
        public RichListBox()
        {
            InitializeComponent();
        }
        //==================属性=====================
        [Description("ListBox数据容器")]
        [DefaultValue(typeof(ItemList), "")]
        public ItemList Items
        {
            get
            {
                return _Items;
            }
            set
            {
                _Items = value;
            }
        }
        
        //============  事件  ====================
        private void RichListBox_Load(object sender, EventArgs e)
        {

            Control.CheckForIllegalCrossThreadCalls = false;
            this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
            this.vScrollBar.Value = 0;
            this.vScrollBar.SmallChange = ITEM_HEIGHT;
            
            this.Items = new ItemList(this);
            this.SelectedInex = -1;
            this.HoveredInex = -1;
            //this.Items.Add(new RichListItem("中文.txt",20,100,RichList.Properties.Resources.device_ipad));
            //this.Items.Add(new RichListItem("中文.txt", 20, 100, RichList.Properties.Resources.device_ipad));
            //this.Items.Add(new RichListItem("中文.txt", 20, 100, RichList.Properties.Resources.device_ipad));
          
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            base.OnPaintBackground(e);
        }

        //===重绘 
        protected void DrawItem(Graphics g, Rectangle rect, RichListItem item,Color bk)
        {

            //g.DrawRectangle(new Pen(Color.Blue), rect);
            //背景
            //g.FillRectangle(new SolidBrush(bk), rect);
            g.FillRectangle(new SolidBrush(Color.FromArgb(128,bk.R,bk.G,bk.B)), rect);


            //图像
            Image img = item._img;
            Image img2 = RichList.Properties.Resources.progress_bk;
            Image img3 = RichList.Properties.Resources.slideblue;
            Image img4 = null;

            img2 = img2.GetThumbnailImage(160, 11, null, IntPtr.Zero);
            int pro_width = 0;
            if (item._totallength != 0)
            {
                pro_width = (int)((double)item._downloaded / item._totallength * 160);
            }
            img3 = img3.GetThumbnailImage( pro_width > 0 ?pro_width:1, 11, null, IntPtr.Zero);
            
            switch(item._status)
            {
                case RichListItem.UNSTART:
                    img4 = RichList.Properties.Resources.wait;
                    break;
                case RichListItem.RUNNING:
                    img4 = RichList.Properties.Resources.downloading;
                    break;
                case RichListItem.COMPLETED:
                    img4 = RichList.Properties.Resources.complete;
                    break;
                case RichListItem.ERROR:
                    img4 = RichList.Properties.Resources.fail;
                    break;
                case RichListItem.SUSPEND:
                    img4 = RichList.Properties.Resources.pause;
                    break;

            } 

            g.DrawImage(img, rect.X + 10, rect.Y + 10);
            g.DrawImage(img2, rect.X + 50, rect.Y + 30);
            g.DrawImage(img3, rect.X + 50, rect.Y + 30);
            g.DrawImage(img4, rect.X + 27, rect.Y + 30);

            //文件名
            StringFormat stringFormat = new StringFormat();
            stringFormat.Alignment = StringAlignment.Near;
            stringFormat.LineAlignment = StringAlignment.Near;

            StringFormat stringFormatRight = new StringFormat();
            stringFormatRight.Alignment = StringAlignment.Far;
            stringFormatRight.LineAlignment = StringAlignment.Far;

            RectangleF rectF = new RectangleF(rect.X + 50, rect.Y + 10, 160, 13);
            g.DrawString(item._filename, new Font(FontFamily.GenericSansSerif, 9), Brushes.DimGray, rectF, stringFormat);
 
            //百分比

            stringFormat.Alignment = StringAlignment.Center;
            stringFormat.LineAlignment = StringAlignment.Center;
            rectF = new RectangleF(rect.X + 50, rect.Y + 30,160,11);
            g.DrawString(string.Format("{0:F2}",(double)item._downloaded/item._totallength*100)+"%", new Font(FontFamily.GenericSansSerif, 9), Brushes.DimGray, rectF, stringFormat);
            
            //详细下载量
            stringFormat.Alignment = StringAlignment.Far;
            stringFormat.LineAlignment = StringAlignment.Far;
            rectF = new RectangleF(rect.X + rect.Width - 120, rect.Y + 10, 120, 13);
            g.DrawString(Util.FormatSize(item._downloaded) + "/" + Util.FormatSize(item._totallength), new Font(FontFamily.GenericSansSerif, 9), Brushes.DimGray, rectF, stringFormatRight);

        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (this.Items.Count * ITEM_HEIGHT > this.Height)
            {
                this.vScrollBar.Visible = true;
            }else
            {
                this.vScrollBar.Visible = false;
            }
            
            int offset = this.vScrollBar.Value ;
            //this.label1.Text = this.vScrollBar.Value.ToString() + ',' + this.vScrollBar.Maximum.ToString();

            //MessageBox.Show(this.Items.Count.ToString());

            List<long> keys = this.Items.Keys;
            int i = 0;
            foreach (long key in keys)
            {
                Color bk;
                
                //显示不出来，不需要画
                if (-offset + i * ITEM_HEIGHT + ITEM_HEIGHT < e.ClipRectangle.Top)
                {
                    i++;
                    continue;
                }else if( -offset + i * ITEM_HEIGHT > e.ClipRectangle.Bottom)
                {
                    break;
                }
                
                if (i % 2 == 1)
                {
                    bk = ODD_COLOR;
                }
                else
                {
                    bk = EVEN_COLOR;
                }
                if (HoveredInex == i)
                {
                    bk = HOVER_COLOR;
                }
                if (SelectedInex == i)
                {
                    bk = SELECTED_COLOR;
                }
                DrawItem(e.Graphics, new Rectangle(0, (int)(-offset + i * ITEM_HEIGHT), (int)(this.Width - 2 - (this.vScrollBar.Visible ? SCROLL_WEIDTH : 0)), ITEM_HEIGHT), Items[key], bk);
            
                ++i;
            }
            
            base.OnPaint(e);
        }

        private void RichListBox_SizeChanged(object sender, EventArgs e)
        {
            //this.vScrollBar1.Height = this.Height;
            vScrollBar_refresh();
            this.Invalidate();
            this.Update();
        }

        //重新计算scrollbar的scale 以及 largechange
        internal void vScrollBar_refresh()
        {
            if (Items == null) return;
            int scale = RichListBox.ITEM_HEIGHT * Items.Count;
            this.vScrollBar.Maximum = scale;
            this.vScrollBar.LargeChange = this.Height;

            //this.label1.Text = Items.Count + "," + vScrollBar.Value + "," + vScrollBar.Maximum;
            // 防止放大的时候 下面留白
            if (scale - this.vScrollBar.Value < this.Height)
            {
                if (scale <= this.Height)
                {
                    this.vScrollBar.Value = 0;
                }
                else
                {
                    this.vScrollBar.Value = scale - this.Height;
                }
            }
        }

        public void UpdateItemAt(int index)
        {
            Rectangle rect = new Rectangle(0, RichListBox.ITEM_HEIGHT * index - this.vScrollBar.Value, this.Width, RichListBox.ITEM_HEIGHT);
            this.Invalidate(rect);
            this.Update();
        }
        public void UpdateItemId(long id)
        {
            int index = Items.GetIndexById(id);
            UpdateItemAt(index);
        }

        private void RichListBox_MouseDown(object sender, MouseEventArgs e)
        {
            this.SelectedInex = TestHit(e.X, e.Y);
        }

        private void RichListBox_MouseMove(object sender, MouseEventArgs e)
        {
            this.HoveredInex = TestHit(e.X, e.Y);
        }

        private void RichListBox_MouseWheel(object sender, MouseEventArgs e)
        {
            if (this.vScrollBar.Visible == false) return ;
            if (e.Delta < 0)
            {
                int next = this.vScrollBar.Value + RichListBox.ITEM_HEIGHT;
                this.vScrollBar.Value = next > vScrollBar.Maximum - vScrollBar.LargeChange ? vScrollBar.Maximum - vScrollBar.LargeChange : next;
            }else
            {
                int next = this.vScrollBar.Value - RichListBox.ITEM_HEIGHT;
                this.vScrollBar.Value = next > 0? next : 0;
            }
            
            this.Invalidate();
            this.Update();
        }

        //测试第几个Item
        private int TestHit(int x, int y)
        {
            int index = (y + this.vScrollBar.Value) / RichListBox.ITEM_HEIGHT;
            if (index >= _Items.Count) return -1;
            return index;
        }

        private void RichListBox_MouseLeave(object sender, EventArgs e)
        {
            this.HoveredInex = -1;
        }

        private void vScrollBar_Scroll(object sender, EventArgs e)
        {
            this.Invalidate();
            this.Update();
        }


    }

    public class ItemList
    {
        Dictionary<long, RichListItem> _data;

        long _nextIdleId;
        RichListBox _richlistbox;
        public ItemList(RichListBox handle)
        {
            _data = new Dictionary<long, RichListItem>();
            _richlistbox = handle;
            _nextIdleId = 0;
        }
        public long GetNextIdleId()
        {
            return _nextIdleId++;
        }
        public int GetIndexById(long id)
        {
            int i=0;
            foreach(long key in _data.Keys)
            {
                if (key == id)
                {
                    return i;
                }
                i++;
            }
            return -1;
        }
        public long GetIdByIndex(int index)
        {
            List<long> keys = _data.Keys.ToList<long>();
            return keys[index];
        }
        public void Add(RichListItem item)
        {
            //if (item._id == -1)
            //{
            //    item._id = GetNextIdleId();
            //}
            _data.Add(item._id,item);
            _richlistbox.vScrollBar_refresh();
            _richlistbox.Invalidate();
            _richlistbox.Update();
        }
        public void Clear()
        {
            _data.Clear();
            _richlistbox.vScrollBar_refresh();
            _richlistbox.Invalidate();
            _richlistbox.Update();
        }
        public void RemoveId(long n)
        {
            _data.Remove(n);
            _richlistbox.vScrollBar_refresh();
            _richlistbox.Invalidate();
            _richlistbox.Update();
        }
        public List<long> Keys
        {
            get
            {
                return _data.Keys.ToList<long>();
            }
        }
        public int Count
        {
            get
            {
                return _data.Count;
            }
        }
        public RichListItem this[long id]
        {
            get
            {
                return _data[id];
            }
            set
            {
                _data[id] = value;
                _richlistbox.vScrollBar_refresh();
                _richlistbox.Invalidate();
                _richlistbox.Update();
            }
        }
        

 

    }

    public class RichListItem : IComparer<RichListItem>
    {
        public const int UNSTART = 0;
        public const int ERROR = 1;
        public const int RUNNING = 2;
        public const int SUSPEND = 3;
        public const int COMPLETED = 4;

        public long _id;
        internal string _filename;
        public long _downloaded;
        public long _totallength;
        public int _status;
        internal Color _bkground;
        internal Image _img;
        public RichListItem()
        {}
        public RichListItem(long id,string filename,long downed,long totallen,Image e)
        {
            _id = id;
            _filename =filename;
            _downloaded = downed;
            _totallength = totallen;
            _img = e;
            _status = UNSTART;
        }
        
        public int Compare(RichListItem a, RichListItem b)
        {
            return (int)(a._id - b._id);
        }
    }
}
